package com.yiwu.netty.util;

import cn.hutool.core.date.DateUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.json.JSONUtil;
import com.yiwu.common.component.RedisComponent;
import com.yiwu.common.domain.dto.ChatMessageDTO;
import com.yiwu.common.domain.entity.ChatMessage;
import com.yiwu.common.domain.entity.ChatSession;
import com.yiwu.common.domain.entity.ChatSessionUser;
import com.yiwu.common.domain.entity.Users;
import com.yiwu.common.enums.MessageTypeEnum;
import com.yiwu.common.enums.ResponseCode;
import com.yiwu.common.exception.BusinessException;
import io.micrometer.common.util.StringUtils;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.exception.RemotingException;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.concurrent.ConcurrentHashMap;


/**
 * @version 1.0
 * @description TODO
 * @date 2024/5/28 23:34
 */
@Component
@Slf4j
public class NettyServerUtil {

    private final static ConcurrentHashMap<String, Channel> USER_CONTEXT_MAP = new ConcurrentHashMap<>();

    private final DefaultMQProducer mq = RocketMQProducerSingleton.getProducer();

    @Resource
    private RedisComponent redisComponent;

    public void addContext(String sessionId, String userId, Channel channel) {
        String channelId = channel.id().toString();
        AttributeKey<String> attributeKey = null;
        if (!AttributeKey.exists(channelId)) {
            attributeKey = AttributeKey.newInstance(channelId);
        } else {
            attributeKey = AttributeKey.valueOf(channelId);
        }
        channel.attr(attributeKey).set(userId);

        USER_CONTEXT_MAP.put(userId, channel);
        if (sessionId != null) {
            redisComponent.saveSession(sessionId, userId);
        }
        redisComponent.saveHeartBeat(userId);
    }

    @Transactional
    public void sendMessage(ChatMessage chatMessage) {

        Long contactId = chatMessage.getReceiverId();
        Long userId = chatMessage.getSenderId();

        // 获取当前的 LocalDateTime
        LocalDateTime localDateTime = LocalDateTime.now();
        long time = DateUtil.date(localDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()).getTime();
        String sessionId = generateSessionId(userId, contactId);
        chatMessage.setSessionId(sessionId);
        chatMessage.setSendTime(time);
        Long sendTime = System.currentTimeMillis();

        //建立 session 联系
        // session
        MessageTypeEnum type = MessageTypeEnum.getType(chatMessage.getMessageType());
        ChatSession chatSession = new ChatSession();
        if (type == MessageTypeEnum.MEDIA_CHAT) {
            chatSession.setLastMessage("[图片]");
        } else if (type == MessageTypeEnum.CHAT) {
            chatSession.setLastMessage(chatMessage.getMessageContent());
        }
        chatSession.setSessionId(sessionId);
        chatSession.setLastReceiveTime(sendTime);

        // sessionUser
        ChatSessionUser chatSessionUser = new ChatSessionUser();
        chatSessionUser.setContactId(contactId);
        chatSessionUser.setUserId(userId);
        chatSessionUser.setSessionId(sessionId);

        // 查找所联系用户姓名
        Users user = new Users();
        Users contactUser = user.selectById(chatSessionUser.getContactId());
        chatSessionUser.setContactName(contactUser.getUserName());
        chatSessionUser.setAvatarUrl(contactUser.getAvatarUrl());


        Users userInfo = user.selectById(chatSessionUser.getUserId());
        ChatSessionUser userChat = new ChatSessionUser();
        userChat.setSessionId(sessionId);
        userChat.setUserId(contactId);
        userChat.setContactId(userId);
        userChat.setContactName(userInfo.getUserName());
        userChat.setAvatarUrl(userInfo.getAvatarUrl());

        //todo 存储对话消息session，如果用户停留在当前页面则保存session，如果离开页面则删除session
        redisComponent.saveSession(sessionId, userId.toString());
        //如果对方的session存在说明在同一聊天框下，只要一人发送消息，就同步双方的心跳
        if (redisComponent.getSession(sessionId, contactId.toString())) {
            redisComponent.saveSession(sessionId, contactId.toString());
        }

        // 存储
        try {
            boolean existence = userChat.existence(userId, contactId);
            if (!existence) {
                chatSessionUser.insert();
            }
            existence = userChat.existence(contactId, userId);
            if (!existence) {
                userChat.insert();
            }
            chatSession.insertOrUpdate();
            send2User(contactId, chatMessage);
        } catch (Exception e) {
            log.warn("{}:cmsImpl sendMessage error", chatMessage.getSenderId());
            throw new BusinessException(ResponseCode.NO_SAVE);
        }
    }

    public void send2User(Long receiverId, ChatMessage chatMessage) {
        Channel channel = USER_CONTEXT_MAP.get(receiverId.toString());
        Channel listChannel = USER_CONTEXT_MAP.get(receiverId + "list");
        ChatMessageDTO chatMessageDTO = new ChatMessageDTO();
        Boolean session = redisComponent.getSession(chatMessage.getSessionId(), receiverId.toString());

        if (channel != null && session) {
            chatMessage.insert();
            BeanUtils.copyProperties(chatMessage, chatMessageDTO);
            chatMessageDTO.setSenderId(chatMessage.getSenderId().toString());
            chatMessageDTO.setFileSize(chatMessage.getFileSize().toString());
            chatMessageDTO.setReceiverId(chatMessage.getReceiverId().toString());
            chatMessageDTO.setSendTime(chatMessage.getSendTime().toString());
            channel.writeAndFlush(new TextWebSocketFrame(JSONUtil.toJsonPrettyStr(chatMessageDTO)))
                    .addListener((ChannelFutureListener) future -> {
                        if (future.isSuccess()) {
                            // 发送成功，更新消息状态
                            chatMessage.setStatus(1);
                            chatMessage.updateById();
                        }
                    });

        } else if (redisComponent.getHeartBeat(receiverId.toString()) == null || session == null) {
            chatMessage.setStatus(0);
            byte[] msg = JSONUtil.toJsonPrettyStr(chatMessage).getBytes();
            Message message = new Message("Message_System", "TagMsg", msg);
            try {
                mq.send(message);
            } catch (MQClientException | RemotingException | MQBrokerException | InterruptedException e) {
                throw new BusinessException(ResponseCode.MQSEND_ERROR);
            }
            String key = chatMessage.getSessionId() + receiverId + "msg";
            redisComponent.saveMessage(key,JSONUtil.toJsonStr(chatMessage),System.currentTimeMillis());
        }
        if (listChannel != null) {
            listChannel.writeAndFlush(new TextWebSocketFrame("13100558"));
        }
    }

    public void removeContext(Channel channel) {
        Attribute<String> attr = channel.attr(AttributeKey.valueOf(channel.id().toString()));
        String userId = attr.get();
        if (!StringUtils.isEmpty(userId)) {
            USER_CONTEXT_MAP.remove(userId);
        }
        redisComponent.removeHeartBeat(userId);
        redisComponent.removeSession(userId);
        //如果当前用户不在线才更新这个，需要检查用户是否在线,更新用户最后离线时间
//        Users users = new Users();
//        users.setLastOffTime(System.currentTimeMillis());
//        users.setId(Long.parseLong(userId));
//        users.updateById();
    }

    public String generateSessionId(Long userId, Long contactId) {
        // 按降序排序
        String sorted = userId > contactId ? userId.toString() + contactId : contactId.toString() + userId;
        // 生成 MD5
        return SecureUtil.md5(sorted);
    }


}
